package cn.xiuxius.embedded.command.flash;

import cn.xiuxius.embedded.command.TaskCommand;
import cn.xiuxius.embedded.config.EasyYamlConfig;
import cn.xiuxius.embedded.keil.KeilProjectXML;
import com.fazecast.jSerialComm.SerialPort;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Callable;

@CommandLine.Command(name = "flash",
        description = "Flush command",
        mixinStandardHelpOptions = true,
        subcommands = {
                BaseFlashCommand.class,
                AdvancedFlashCommand.class
        }
)
@Slf4j
@Getter
public class FlashCommand implements Callable<Integer> {

    @CommandLine.ParentCommand
    private TaskCommand taskCommand;

    @CommandLine.Mixin
    private FlashCommandOptions options;

    private File stcgal;

    private EasyYamlConfig config;

    private KeilProjectXML keilProjectXML;

    private File hexFile;

    @CommandLine.Spec
    CommandLine.Model.CommandSpec spec;


    @Override
    public Integer call() {
        if (!spec.commandLine().getParseResult().hasSubcommand()) {
            CommandLine.usage(this, System.out);
        }
        return 0;
    }

    public int prepare() {
        if (taskCommand == null) {
            CommandLine.usage(this, System.out);
            return 1;
        }

        if (taskCommand.prepare() != 0) {
            return 1;
        }

        if (!spec.commandLine().getParseResult().hasSubcommand()) {
            CommandLine.usage(this, System.out);
            return 1;
        }

        String jarHome = taskCommand.getJarHome();
        // 检查 stcgal 是否存在
        stcgal = new File(Paths.get(jarHome, "stcgal.exe").toString());
        if (!stcgal.exists()) {
            log.error("stcgal.exe does not exist: {}", stcgal.getAbsolutePath());
            return 1;
        }
        config = taskCommand.getConfig();
        keilProjectXML = taskCommand.getKeilProjectXML();
        if (this.options.getChipProtocol() == null || this.options.getChipProtocol().isEmpty()) {
            if (config.getStcgl().getStcType() == null) {
                options.setChipProtocol("AUTO");
            } else {
                options.setChipProtocol(config.getStcgl().getStcType().getValue().getValue());
            }
        }

        Path rootPath = Path.of(config.getKeilProject().getRootPath());
        Path outputDirRelative = rootPath.resolve(keilProjectXML.getOutputDirectory());
        Path hexFile = outputDirRelative.resolve(keilProjectXML.getOutputName() + ".hex").toAbsolutePath();
        if (!Files.exists(hexFile)) {
            log.error(".hex file does not exist: {}", hexFile);
            return 1;
        }
        this.hexFile = hexFile.toFile();
        if (options.getPortName() == null || options.getPortName().isEmpty()) {
            if (config.getStcgl().getPort() == null) {
                log.warn("Port not specified, will try to find the first available port.");
                options.setPortName("AUTO");
            } else {
                options.setPortName(config.getStcgl().getPort().getValue());
            }
        }
        if (options.getPortName().equals("AUTO")) {
            SerialPort selectPort;
            log.info("Port is auto, will try to find the first available port.");
            List<SerialPort> serialPorts = Arrays.stream(SerialPort.getCommPorts()).toList();
            if (serialPorts.isEmpty()) {
                log.error("No available ports found.");
                return 1;
            }
            //如果只有一个则选择这个
            if (serialPorts.size() == 1) {
                //找到唯一的端口 已自动选择此端口
                selectPort = serialPorts.getFirst();
                log.info("✅ Port is auto, find the only available port: {}", selectPort.getSystemPortName());
            } else {
                //找到多个 用户自己选择
                log.info("✅ Find multiple available ports:");
                for (int i = 0; i < serialPorts.size(); i++) {
                    log.info("{}: {}", i, serialPorts.get(i).getSystemPortName());
                }
                log.info("Please select the port you want to use:");
                Scanner scanner = new Scanner(System.in);
                if (scanner.hasNextInt()) {
                    int portIndex = scanner.nextInt();
                    if (portIndex < 0 || portIndex >= serialPorts.size()) {
                        log.error("Invalid input, please enter a number between 0 and {}.", serialPorts.size() - 1);
                        return 1;
                    }
                    selectPort = serialPorts.get(portIndex);
                } else {
                    log.error("Invalid input, please enter a number.");
                    return 1;
                }
            }
            options.setPortName(selectPort.getSystemPortName());
        }

        return 0;
    }

}
